#include "server_handle.h"
#include "client_handle.h"
#include "ievent.h"
#include <cerrno>
#include <cstring>
#include <cassert>
#include <fcntl.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

using namespace std;

static bool SetNonBlocking(int sock) {
    int opts;
    opts = fcntl(sock, F_GETFL);
    if (opts < 0) {
        return false;
    }
    opts = opts | O_NONBLOCK;
    if (fcntl(sock, F_SETFL, opts) < 0) {
        return false;
    }
    return true;
}

ServerHandle::ServerHandle(IEvent* event)
: mEvent(event)
, mSock(-1) {
    assert(mEvent != nullptr);
}

ServerHandle::~ServerHandle() {
    if (mSock >= 0) {
        shutdown(mSock, 2);
        close(mSock);
        mSock = -1;
    }
}

tuple<bool, string> ServerHandle::Init(const std::string& ip, int port, int backlog) {
    assert(port > 0);
    assert(backlog > 0);
    assert(mSock < 0);

    string errorMsg;
    do {
        mSock = socket(AF_INET, SOCK_STREAM, 0);
        if (mSock < 0) {
            errorMsg = "Fail to create socket: " + string(strerror(errno));
            break;
        }

        if (!SetNonBlocking(mSock)) {
            errorMsg = "Fail to set nonblocking: " + string(strerror(errno));
            break;
        }

        struct sockaddr_in addr;
        memset(&addr, 0, sizeof(addr));
        addr.sin_family = AF_INET;
        addr.sin_port = htons(port);
        addr.sin_addr.s_addr = ip.empty() ? INADDR_ANY : inet_addr(ip.c_str());

        int val = 1;
        setsockopt(mSock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
        if (bind(mSock, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
            errorMsg = "Fail to bind addr: " + string(strerror(errno));
            break;
        }

        if (listen(mSock, backlog) < 0) {
            errorMsg = "Fail to listen: " + string(strerror(errno));
            break;
        }
        return make_tuple(true, "");
    } while (0);

    if (mSock >= 0) {
        close(mSock);
        mSock = -1;
    }
    return make_tuple(false, errorMsg);
}

int ServerHandle::GetSock() const {
    return mSock;
}

void ServerHandle::OnIn() {
    struct sockaddr_in addr;
    socklen_t size = sizeof(struct sockaddr_in);
    while (1) {
        memset(&addr, 0, sizeof(addr));
        int sock = accept(mSock, (struct sockaddr*)&addr, &size);
        if (sock < 0) {
            if (errno == EAGAIN) {
                break;
            } else if (errno == EINTR) {
                continue;
            } else {
                mEvent->OnLog(nullptr, "Fail to accept socket: " + string(strerror(errno)));
                return;
            }
        } else if (SetNonBlocking(sock)) {
            ClientHandle* h = new ClientHandle(mEvent, sock);
            if (nullptr == h) {
                mEvent->OnLog(nullptr, "Fail to new client id");
            }
        } else {
            mEvent->OnLog(nullptr, "Fail to set nonblocking: " + string(strerror(errno)));
            close(sock);
        }
    }
}

void ServerHandle::OnOut() {
}
